package stone926.mods.more_enchantments.spawn;

import net.minecraft.entity.EntityType;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.predicate.entity.LocationPredicate;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.gen.feature.ConfiguredStructureFeatureKeys;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;
import stone926.mods.more_enchantments.interfaces.IEntitySpawnedByFlower;
import stone926.mods.more_enchantments.mixins.accessors.EnderDragonFightAccessor;
import stone926.mods.more_enchantments.util.RandomUtil;
import stone926.mods.more_enchantments.util.StructurePredicate;

import java.util.Optional;
import java.util.Random;

import static stone926.mods.more_enchantments.spawn.SimplePool.Entry.of;

public final class BlackMandalaSpawner {

  private static final SimplePool OVERWORLD_BASE = SimplePool.of(
    of(EntityType.WITCH, EntityInitializers.WITCH, 5),
    of(EntityType.WITCH, EntityInitializers.WITCH_DROPS_CORNEL, 4),
    of(EntityType.CREEPER, EntityInitializers.CREEPER, 8)
  );

  public static SimplePool OVERWORLD_WARM = new SimplePool(OVERWORLD_BASE).putAll(
    of(EntityType.ZOMBIE, EntityInitializers.ZOMBIE_USE_WEAPON, 37),
    of(EntityType.ZOMBIE, EntityInitializers.ZOMBIE_USE_TOOL, 7),
    of(EntityType.ZOMBIE_VILLAGER, EntityInitializers.ZOMBIE_VILLAGER, 2),
    of(EntityType.SKELETON, EntityInitializers.SKELETON_USING_BOW, 42),
    of(EntityType.SKELETON, EntityInitializers.SKELETON_USING_WEAPON, 8)
  );

  public static SimplePool OVERWORLD_COLD = new SimplePool(OVERWORLD_BASE).putAll(
    of(EntityType.ZOMBIE, EntityInitializers.ZOMBIE_USE_WEAPON, 33),
    of(EntityType.ZOMBIE, EntityInitializers.ZOMBIE_USE_TOOL, 7),
    of(EntityType.ZOMBIE_VILLAGER, EntityInitializers.ZOMBIE_VILLAGER, 6),
    of(EntityType.STRAY, EntityInitializers.SKELETON_USING_BOW, 42),
    of(EntityType.STRAY, EntityInitializers.SKELETON_USING_WEAPON, 8)
  );

  public static SimplePool HIGH_DOWNFALL = SimplePool.of(
    of(EntityType.DROWNED, EntityInitializers.DROWNED, 10)
  );

  public static SimplePool NORMAL_DOWNFALL = SimplePool.of(
    of(EntityType.DROWNED, EntityInitializers.DROWNED, 5)
  );

  public static SimplePool RAIN_OR_THUNDER = SimplePool.of(
    of(EntityType.DROWNED, EntityInitializers.DROWNED, 8)
  );

  public static SimplePool VILLAGE = SimplePool.of(
    of(EntityType.ZOMBIE_VILLAGER, EntityInitializers.ZOMBIE_VILLAGER, 30)
  );

  public static SimplePool THE_END = SimplePool.of(
    of(EntityType.ENDERMAN, EntityInitializers.ENDERMAN, 130),
    of(EntityType.SHULKER, EntityInitializers.SHULKER, 20)
  );

  public static SimplePool THE_END_DRAGON_FIGHT = SimplePool.of(
    of(EntityType.ENDERMAN, EntityInitializers.ENDERMAN_DRAGON_FIGHT, 100),
    of(EntityType.SHULKER, EntityInitializers.SHULKER, 50)
  );

  public static SimplePool NETHER_WASTES = SimplePool.of(
    of(EntityType.GHAST, EntityInitializers.discardChance(0.5), 5),
    of(EntityType.ZOMBIFIED_PIGLIN, EntityInitializers.ZOMBIFIED_PIGLIN, 50)
  );

  public static SimplePool SOUL_SAND_VALLEY = SimplePool.of(
    of(EntityType.SKELETON, EntityInitializers.SKELETON_USING_BOW, 11),
    of(EntityType.GHAST, EntityInitializers.discardChance(0.85), 1)
  );

  public static SimplePool BASALT_DELTA = SimplePool.of(
    of(EntityType.MAGMA_CUBE, EntityInitializers.MAGMA_CUBE, 1)
  );

  public static SimplePool WARPED_FOREST = SimplePool.of(
    of(EntityType.ENDERMAN, EntityInitializers.discardChance(0.84), 1)
  );

  public static SimplePool CRIMSON_FOREST = SimplePool.of(
    of(EntityType.PIGLIN, EntityInitializers.PIGLIN, 50),
    of(EntityType.ZOMBIFIED_PIGLIN, EntityInitializers.ZOMBIFIED_PIGLIN, 15),
    of(EntityType.PIGLIN_BRUTE, EntityInitializers.PIGLIN_BRUTE, 5),
    of(EntityType.GHAST, EntityInitializers.discardChance(0.5), 1),
    of(EntityType.HOGLIN, EntityInitializers.HOGLIN, 30),
    of(EntityType.ZOGLIN, EntityInitializers.HOGLIN, 3)
  );

  public static SimplePool MISC = SimplePool.of(
    of(EntityType.ZOMBIE, EntityInitializers.EMPTY, 1)
  );

  public static SimplePool NETHER_MISC = SimplePool.of(
    of(EntityType.ZOMBIFIED_PIGLIN, EntityInitializers.ZOMBIFIED_PIGLIN, 1)
  );

  public static SimplePool FORTRESS = SimplePool.of(
    of(EntityType.WITHER_SKELETON, EntityInitializers.WITHER_SKELETON, 50),
    of(EntityType.BLAZE, EntityInitializers.EMPTY, 15)
  );

  public static SimplePool BASTION_REMNANT = SimplePool.of(
    of(EntityType.PIGLIN_BRUTE, EntityInitializers.PIGLIN_BRUTE_BASTION_REMNANT, 10)
  );

  public static SimplePool SHIPWRECK = SimplePool.of(
    of(EntityType.DROWNED, EntityInitializers.DROWNED, 100)
  );

  public static SimplePool OVERWORLD_RUINED_PORTAL = SimplePool.of(
    of(EntityType.ZOMBIFIED_PIGLIN, EntityInitializers.EMPTY, 1)
  );

  public static SimplePool END_CITY = SimplePool.of(
    of(EntityType.SHULKER, EntityInitializers.SHULKER_HAS_TREASURE, 20)
  );

  public static SimplePool PILLAGER_OUTPOST = SimplePool.of(
    of(EntityType.PILLAGER, EntityInitializers.PILLAGER, 10),
    of(EntityType.EVOKER, EntityInitializers.EVOKER, 10),
    of(EntityType.VINDICATOR, EntityInitializers.VINDICATOR, 10),
    of(EntityType.ILLUSIONER, EntityInitializers.ILLUSIONER, 10),
    of(EntityType.WITCH, EntityInitializers.WITCH, 10),
    of(EntityType.VEX, EntityInitializers.VEX, 5)
  );

  public static SimplePool MONUMENT = SimplePool.of(
    of(EntityType.GUARDIAN, EntityInitializers.GUARDIAN, 5),
    of(EntityType.ELDER_GUARDIAN, EntityInitializers.ELDER_GUARDIAN, 2),
    of(EntityType.DROWNED, EntityInitializers.ZOMBIE_USE_WEAPON, 4),
    of(EntityType.DROWNED, EntityInitializers.DROWNED, 3)
  );

  public static SimplePool getSpawnPool(ServerWorld world, BlockPos pos) {
    Biome biome = world.getBiome(pos).value();
    Optional<RegistryKey<Biome>> key = world.getRegistryManager().get(Registry.BIOME_KEY).getKey(biome);
    SimplePool spawnPool = new SimplePool(MISC);
    if (key.isPresent()) {
      RegistryKey<Biome> biomeRegistryKey = key.get();
      double x = pos.getX();
      double y = pos.getY();
      double z = pos.getZ();
      if (world.getRegistryKey().equals(World.OVERWORLD)) {
        if (biome.isCold(pos)) {
          spawnPool = new SimplePool(OVERWORLD_COLD);
        } else {
          spawnPool = new SimplePool(OVERWORLD_WARM);
        }
        if (biome.getDownfall() >= 0.8) {
          spawnPool.putAll(HIGH_DOWNFALL.getEntries());
        } else if (biome.getDownfall() > 0.3) {
          spawnPool.putAll(NORMAL_DOWNFALL.getEntries());
        }
        if (biome.getDownfall() > 0.3 && (world.isRaining() || world.isThundering())) {
          spawnPool.putAll(RAIN_OR_THUNDER.getEntries());
        }
        if (LocationPredicate.feature(ConfiguredStructureFeatureKeys.SHIPWRECK).test(world, x, y, z)) {
          spawnPool.putAll(SHIPWRECK.getEntries());
        } else if (LocationPredicate.feature(ConfiguredStructureFeatureKeys.RUINED_PORTAL).test(world, x, y, z)) {
          spawnPool.putAll(OVERWORLD_RUINED_PORTAL.getEntries());
        } else if (StructurePredicate.test(world, ConfiguredStructureFeatureKeys.PILLAGER_OUTPOST, x, y, z)) {
          spawnPool = new SimplePool(PILLAGER_OUTPOST);
        } else if (LocationPredicate.feature(ConfiguredStructureFeatureKeys.MONUMENT).test(world, x, y, z)) {
          spawnPool = new SimplePool(MONUMENT);
        } else if (
          StructurePredicate.test(world, ConfiguredStructureFeatureKeys.VILLAGE_DESERT, x, y, z) ||
            StructurePredicate.test(world, ConfiguredStructureFeatureKeys.VILLAGE_PLAINS, x, y, z) ||
            StructurePredicate.test(world, ConfiguredStructureFeatureKeys.VILLAGE_SAVANNA, x, y, z) ||
            StructurePredicate.test(world, ConfiguredStructureFeatureKeys.VILLAGE_SNOWY, x, y, z) ||
            StructurePredicate.test(world, ConfiguredStructureFeatureKeys.VILLAGE_TAIGA, x, y, z)
        ) {
          spawnPool.putAll(VILLAGE.getEntries());
        }
      } else if (world.getRegistryKey().equals(World.END)) {
        EnderDragonFightAccessor enderDragonFight = (EnderDragonFightAccessor) world.getEnderDragonFight();
        if (enderDragonFight != null && !enderDragonFight.isDragonKilled()) {
          spawnPool = new SimplePool(THE_END_DRAGON_FIGHT);
        } else {
          spawnPool = new SimplePool(THE_END);
        }
        if (LocationPredicate.feature(ConfiguredStructureFeatureKeys.END_CITY).test(world, x, y, z)) {
          spawnPool.putAll(END_CITY.getEntries());
        }
      } else if (world.getRegistryKey().equals(World.NETHER)) {
        if (biomeRegistryKey.equals(BiomeKeys.SOUL_SAND_VALLEY)) {
          spawnPool = new SimplePool(SOUL_SAND_VALLEY);
        } else if (biomeRegistryKey.equals(BiomeKeys.NETHER_WASTES)) {
          spawnPool = new SimplePool(NETHER_WASTES);
        } else if (biomeRegistryKey.equals(BiomeKeys.CRIMSON_FOREST)) {
          spawnPool = new SimplePool(CRIMSON_FOREST);
        } else if (biomeRegistryKey.equals(BiomeKeys.WARPED_FOREST)) {
          spawnPool = new SimplePool(WARPED_FOREST);
        } else if (biomeRegistryKey.equals(BiomeKeys.BASALT_DELTAS)) {
          spawnPool = new SimplePool(BASALT_DELTA);
        } else {
          spawnPool = new SimplePool(NETHER_MISC);
        }
        if (StructurePredicate.test(world, ConfiguredStructureFeatureKeys.FORTRESS, x, y, z)) {
          spawnPool.putAll(FORTRESS.getEntries());
        } else if (LocationPredicate.feature(ConfiguredStructureFeatureKeys.BASTION_REMNANT).test(world, x, y, z)) {
          spawnPool.putAll(BASTION_REMNANT.getEntries());
        }
      }
    }
    return spawnPool;
  }

  public static void spawnMonster(ServerWorld world, BlockPos pos, Random random, int weather, int daylight) {
    SimplePool spawnPool = getSpawnPool(world, pos);
    int dragonFight1 = 0;
    int dragonFight2 = 0;
    EnderDragonFightAccessor enderDragonFight = (EnderDragonFightAccessor) world.getEnderDragonFight();
    if (world.getRegistryKey().equals(World.END) && enderDragonFight != null && !(enderDragonFight).isDragonKilled()) {
      dragonFight1 += RandomUtil.nextInt(1, 2, random);
      dragonFight2 += RandomUtil.nextInt(5, 10, random);
    }
    int spawnCount = !RandomUtil.isPossible(Math.max(1, 27 - 3 * weather - daylight), random) ?
      (1 + dragonFight1) :
      (16 + weather + random.nextInt(21 + 5 * weather) + dragonFight2);
    for (int i = 0; i < spawnCount; i++) {
      SimplePool.Entry<? extends MobEntity> entry = spawnPool.getRandomEntry(random);
      EntityType<? extends MobEntity> entityType = entry.entityType();
      EntityInitializer entityInitializer = entry.entityInitializer();
//      EntityInitializer<? extends MobEntity> entityInitializer = entry.entityInitializer();
      if (entityType != null) {
        MobEntity entity = entityType.create(world);
        if (entity != null) {
          entity.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5);
          ((IEntitySpawnedByFlower) entity).setSpawnedByMandala(true);
          entityInitializer.initialize(entity, random);
//          entityInitializer.initialize(entityType.create(world), random);
          if (spawnCount > 1) {
            entity.addVelocity(random.nextGaussian() / 3D, random.nextDouble() * 1.4, random.nextGaussian() / 3D);
          }
          world.spawnEntity(entity);
        }
      } else { MoreEnchantmentsMod.LOGGER.error("BlackMandalaSpawnException:NULL!"); }
    }
  }

}
